using System;
using System.IO;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Security.Cryptography;
using UnityEngine;
using UnityEngine.EventSystems;

namespace E.Tool
{
    public static class Utility
    {
        #region 字符串处理
        /// <summary>
        /// 漂亮的打印秒数，作为小时：分钟：秒（.milliseconds / 100）s
        /// </summary>
        public static string PrettySeconds(float seconds)
        {
            TimeSpan t = TimeSpan.FromSeconds(seconds);
            string res = "";
            if (t.Days > 0) res += t.Days + "d";
            if (t.Hours > 0) res += " " + t.Hours + "h";
            if (t.Minutes > 0) res += " " + t.Minutes + "m";
            // 0.5s, 1.5s etc. if any milliseconds. 1s, 2s etc. if any seconds
            if (t.Milliseconds > 0) res += " " + t.Seconds + "." + (t.Milliseconds / 100) + "s";
            else if (t.Seconds > 0) res += " " + t.Seconds + "s";
            // if the string is still empty because the value was '0', then at least
            // return the seconds instead of returning an empty string
            return res != "" ? res : "0s";
        }
        /// <summary>
        /// 解析字符串中的最后一个大写名词，例如
        /// EquipmentWeaponBow => Bow => 弓
        /// EquipmentShield => Shield => 盾
        /// </summary>
        public static string ParseLastNoun(string text)
        {
            MatchCollection matches = new Regex(@"([A-Z][a-z]*)").Matches(text);
            return matches.Count > 0 ? matches[matches.Count - 1].Value : "";
        }
        #endregion

        #region 数字处理
        /// <summary>
        /// 限定整型数字范围，仅包含最小值
        /// </summary>
        /// <param name="value"></param>
        /// <param name="min"></param>
        /// <returns></returns>
        public static int ClampMin(int value, int min)
        {
            if (value < min) return min;
            return value;
        }
        /// <summary>
        /// 限定浮点数字范围，仅包含最小值
        /// </summary>
        /// <param name="value"></param>
        /// <param name="min"></param>
        /// <returns></returns>
        public static float ClampMin(float value, float min)
        {
            if (value < min) return min;
            return value;
        }
        /// <summary>
        /// 限定整型数字范围，仅包含最大值
        /// </summary>
        /// <param name="value"></param>
        /// <param name="min"></param>
        /// <param name="max"></param>
        /// <returns></returns>
        public static int ClampMax(int value, int max)
        {
            if (value > max) return max;
            return value;
        }
        /// <summary>
        /// 限定浮点数字范围，仅包含最大值
        /// </summary>
        /// <param name="value"></param>
        /// <param name="min"></param>
        /// <param name="max"></param>
        /// <returns></returns>
        public static float ClampMax(float value, float max)
        {
            if (value > max) return max;
            return value;
        }
        /// <summary>
        /// 限定整型数字范围，包含最小值和最大值
        /// </summary>
        /// <param name="value"></param>
        /// <param name="min"></param>
        /// <param name="max"></param>
        /// <returns></returns>
        public static int Clamp(int value, int min, int max)
        {
            if (value < min) return min;
            if (value > max) return max;
            return value;
        }
        /// <summary>
        /// 限定浮点数字范围，包含最小值和最大值
        /// </summary>
        /// <param name="value"></param>
        /// <param name="min"></param>
        /// <param name="max"></param>
        /// <returns></returns>
        public static float Clamp(float value, float min, float max)
        {
            if (value < min) return min;
            if (value > max) return max;
            return value;
        }
        public static bool TryClamp(float value, float min, float max, out float result)
        {
            if (value < min)
            {
                result = min;
                return false;
            }
            if (value > max)
            {
                result = max;
                return false;
            }
            result = value;
            return true;
        }
        #endregion

        #region 数组处理
        /// <summary>
        /// 获取数组中最小值的索引
        /// </summary>
        /// <param name="array">数组</param>
        /// <returns>最小值的索引</returns>
        public static int IndexMin(List<float> array)
        {
            return array.IndexOf(array.Min());
        }
        /// <summary>
        /// 获取时间数组中最新值的索引
        /// </summary>
        /// <param name="array">数组</param>
        /// <returns>最新值的索引</returns>
        public static int IndexLatest(List<DateTime> array)
        {
            return array.IndexOf(array.Max());
        }
        #endregion

        #region 交互处理
        /// <summary>
        /// 是否有键抬起
        /// </summary>
        /// <param name="keys"></param>
        /// <returns></returns>
        public static bool AnyKeyUp(KeyCode[] keys)
        {
            return keys.Any(k => Input.GetKeyUp(k));
        }
        /// <summary>
        /// 是否有键按下
        /// </summary>
        /// <param name="keys"></param>
        /// <returns></returns>
        public static bool AnyKeyDown(KeyCode[] keys)
        {
            return keys.Any(k => Input.GetKeyDown(k));
        }
        /// <summary>
        /// 是否有键按住
        /// </summary>
        /// <param name="keys"></param>
        /// <returns></returns>
        public static bool AnyKeyPressed(KeyCode[] keys)
        {
            return keys.Any(k => Input.GetKey(k));
        }
        /// <summary>
        ///  硬鼠标滚动在所有平台之间保持一致
        /// Input.GetAxis（“ Mouse ScrollWheel”）和 Input.GetAxisRaw（“ Mouse ScrollWheel”）
        /// 两者都返回值，例如standalone 返回0.01，WebGL返回0.5，导致WebGL等的缩放速度过快。
        /// 通常，GetAxisRaw应该返回-1,0,1，但不适用于滚动
        /// </summary>
        /// <returns></returns>
        public static float GetAxisRawScrollUniversal()
        {
            float scroll = Input.GetAxisRaw("Mouse ScrollWheel");
            if (scroll < 0) return -1;
            if (scroll > 0) return 1;
            return 0;
        }
        /// <summary>
        /// 触控两指捏检测
        /// </summary>
        public static float GetPinch()
        {
            if (Input.touchCount == 2)
            {
                // Store both touches.
                Touch touchZero = Input.GetTouch(0);
                Touch touchOne = Input.GetTouch(1);

                // Find the position in the previous frame of each touch.
                Vector2 touchZeroPrevPos = touchZero.position - touchZero.deltaPosition;
                Vector2 touchOnePrevPos = touchOne.position - touchOne.deltaPosition;

                // Find the magnitude of the vector (the distance) between the touches in each frame.
                float prevTouchDeltaMag = (touchZeroPrevPos - touchOnePrevPos).magnitude;
                float touchDeltaMag = (touchZero.position - touchOne.position).magnitude;

                // Find the difference in the distances between each frame.
                return touchDeltaMag - prevTouchDeltaMag;
            }
            return 0;
        }
        /// <summary>
        /// 检查光标是否在UI或OnGUI元素上
        /// 注意：对于UI，这仅在UI的CanvasGroup阻止Raycasts时有效
        /// 注意：对于OnGUI：hotControl仅在单击时设置，而不在缩放时设置
        public static bool IsCursorOverUI()
        {
            // IsPointerOverGameObject check for left mouse (default)
            if (EventSystem.current.IsPointerOverGameObject())
                return true;

            // IsPointerOverGameObject check for touches
            for (int i = 0; i < Input.touchCount; ++i)
                if (EventSystem.current.IsPointerOverGameObject(Input.GetTouch(i).fingerId))
                    return true;

            // OnGUI check
            return GUIUtility.hotControl != 0;
        }
        #endregion

        #region 对象处理
        /// <summary>
        /// 计算两个对象之间的最近距离。如果实体很大，定位点距离边缘的厚度无法忽略。
        ///    
        /// Vector2.Distance(a.transform.position, b.transform.position):
        ///    _____        _____
        ///   |     |      |     |
        ///   |  x==|======|==x  |
        ///   |_____|      |_____|
        ///
        /// ClosestDistance(a.collider, b.collider):
        ///    _____        _____
        ///   |     |      |     |
        ///   |     |x====x|     |
        ///   |_____|      |_____|
        ///
        /// </summary>
        public static float ClosestDistance(Collider2D a, Collider2D b)
        {
            return Vector2.Distance(a.ClosestPointOnBounds(b.transform.position), b.ClosestPointOnBounds(a.transform.position));
        }
        /// <summary>
        /// 忽略指定对象进行2D射线检测
        /// </summary>
        /// <param name="ray">射线</param>
        /// <param name="ignore">忽略目标，通常是玩家对象自己</param>
        /// <returns></returns>
        public static RaycastHit2D Raycast2DWithout(Ray ray, GameObject ignore)
        {
            // remember layers
            Dictionary<Transform, int> backups = new Dictionary<Transform, int>();

            // set all to ignore raycast
            foreach (Transform tf in ignore.GetComponentsInChildren<Transform>(true))
            {
                backups[tf] = tf.gameObject.layer;
                tf.gameObject.layer = LayerMask.NameToLayer("Ignore Raycast");
            }

            // raycast
            RaycastHit2D result = Physics2D.GetRayIntersection(ray);

            // restore layers
            foreach (KeyValuePair<Transform, int> kvp in backups)
                kvp.Key.gameObject.layer = kvp.Value;

            return result;
        }
        /// <summary>
        /// 获取动画朝向，返回四个方向上下左右之一
        /// </summary>
        public static Vector2 GetDirection(Vector2 vector, Vector2 defaultVector)
        {
            // zero?
            if (vector == Vector2.zero) return defaultVector;

            // normalize
            vector = vector.normalized;

            // quantize
            // -> right?
            if (vector.x > 0)
            {
                if (vector.y > 0.5f) return Vector2.up;
                if (vector.y < -0.5f) return Vector2.down;
                return Vector2.right;
                // -> left?
            }
            else
            {
                if (vector.y > 0.5f) return Vector2.up;
                if (vector.y < -0.5f) return Vector2.down;
                return Vector2.left;
            }
        }
        #endregion


        #region 文件夹处理
        public static void CreateFolder(string path)
        {
            if (Directory.Exists(path))
            {
                //Debug.Log("文件夹已存在 " + path);
            }
            else
            {
                Directory.CreateDirectory(path);
            }
        }
        public static void DeleteFolder(string path)
        {
            if (Directory.Exists(path))
            {
                Directory.Delete(path);
            }
            else
            {
                Debug.LogError("文件夹不存在 " + path);
            }
        }
        public static void OpenFolder(string path, bool create = true)
        {
            if (!Directory.Exists(path))
            {
                if (create)
                {
                    Directory.CreateDirectory(path);
                }
                else
                {
                    Debug.Log("文件夹不存在 " + path);
                    return;
                }
            }
            Debug.Log("打开文件夹 " + path);
            path = path.Replace("/", "\\");
            System.Diagnostics.Process.Start("explorer.exe", path);
        }
        #endregion

        #region 文件处理
        public static List<FileInfo> LoadFiles(string folderName, string fileExtention)
        {
            string folderPath = Application.persistentDataPath + (string.IsNullOrEmpty(folderName) ? "" : ("/" + folderName));
            CreateFolder(folderPath);

            //获取指定路径下面的所有资源文件
            DirectoryInfo direction = new DirectoryInfo(folderPath);
            FileInfo[] fis = direction.GetFiles("*", SearchOption.AllDirectories);
            List<FileInfo> files = new List<FileInfo>();
            int count = 0;
            for (int i = 0; i < fis.Length; i++)
            {
                if (fis[i].Name.EndsWith(fileExtention))
                {
                    files.Add(fis[i]);
                    count++;
                }
            }
            Debug.Log("已载入 " + count + " 个 " + fileExtention + " 文件");
            return files;
        }
        public static string ReadFile(string path)
        {
            if (!File.Exists(path))
            {
                Debug.LogError("文件不存在 " + path);
                return "";
            }
            return File.ReadAllText(path);
        }
        public static string ReadFile(FileInfo file)
        {
            if (!file.Exists)
            {
                Debug.LogError("文件不存在 " + file.FullName);
                return "";
            }
            return File.ReadAllText(file.FullName);
        }
        public static void WriteFile(string path, string content)
        {
            File.WriteAllText(path, content);
        }
        public static void WriteFile(FileInfo file, string content)
        {
            File.WriteAllText(file.FullName, content);
        }
        #endregion

        #region 方法处理
        private static Dictionary<KeyValuePair<Type, string>, MethodInfo[]> lookup = new Dictionary<KeyValuePair<Type, string>, MethodInfo[]>();
        /// <summary>
        /// 通过反射通过前缀调用多个函数。如果object = null，则也适用于静态类。缓存它，以便足够快来进行更新调用
        /// </summary>
        public static MethodInfo[] GetMethodsByPrefix(Type type, string methodPrefix)
        {
            KeyValuePair<Type, string> key = new KeyValuePair<Type, string>(type, methodPrefix);
            if (!lookup.ContainsKey(key))
            {
                MethodInfo[] methods = type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance)
                                           .Where(m => m.Name.StartsWith(methodPrefix))
                                           .ToArray();
                lookup[key] = methods;
            }
            return lookup[key];
        }
        public static void InvokeMany(Type type, object onObject, string methodPrefix, params object[] args)
        {
            foreach (MethodInfo method in GetMethodsByPrefix(type, methodPrefix))
                method.Invoke(onObject, args.ToArray());
        }
        #endregion
    }
}